Skip to content

Comments

[REFACTOR] Redisearch로 매칭 알고리즘 리팩토링#373

Merged
Yoonchulchung merged 2 commits intodevfrom
refactor/372-match-type
Feb 11, 2026
Merged

[REFACTOR] Redisearch로 매칭 알고리즘 리팩토링#373
Yoonchulchung merged 2 commits intodevfrom
refactor/372-match-type

Conversation

@Yoonchulchung
Copy link
Member

Summary

기존 Redis를 캐싱 형태로 사용하여, 모든 연산을 어플리케이션 서버에서 진행하던 것을 Redisearch를 이용해 Redis에서 연산을 수행하도록 로직을 수정했습니다.

Changes

  • 서버 기반 알고리즘 추론 방식에서 Redisearch로 매칭 알고리즘 수정
  • 유저 타입 알고리즘 추가

Type of Change

  • Bug fix (기존 기능에 영향을 주지 않는 버그 수정)
  • New feature (기존 기능에 영향을 주지 않는 새로운 기능 추가)
  • Breaking change (기존 기능에 영향을 주는 수정)
  • Refactoring (기능 변경 없는 코드 개선)
  • Documentation (문서 수정)
  • Chore (빌드, 설정 등 기타 변경)
  • Release (develop → main 배포)

Related Issues

#372

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Yoonchulchung, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 매칭 알고리즘의 핵심 로직을 기존 어플리케이션 서버 기반에서 Redisearch 기반으로 전환하여 성능과 확장성을 크게 개선하는 리팩토링을 포함합니다. 또한, 사용자 태그 데이터를 활용하여 더욱 세분화된 사용자 유형을 분류하는 새로운 알고리즘을 도입하여 매칭의 정확도를 높였습니다. 전반적으로 시스템의 효율성을 증대하고 사용자 경험을 향상시키는 데 중점을 두었습니다.

Highlights

  • Redisearch 기반 매칭 알고리즘 도입: 기존 어플리케이션 서버에서 처리하던 매칭 로직을 Redisearch를 활용하여 Redis에서 직접 연산을 수행하도록 변경했습니다. 이는 매칭 성능 향상 및 서버 부하 감소에 기여합니다.
  • Redisearch 스키마 확장: Redisearch 인덱스에 minCreatorHeight, maxCreatorHeight와 같은 연속형 태그 필드와 preferredBodyTypeTags, preferredTopSizeTags, preferredBottomSizeTags, preferredContentsAverageViewsTags, preferredContentsAgeTags, preferredContentsGenderTags, preferredContentsLengthTags와 같은 이산형 태그 필드가 추가되었습니다.
  • 새로운 사용자 유형 분류 알고리즘 구현: 사용자의 태그 데이터를 기반으로 '유연한 연출가', '섬세한 설계자', '재치있는 스토리텔러', '도전적인 실험가' 등 4가지 사용자 유형을 더 정교하게 분류하는 로직이 MatchServiceImpl에 추가되었습니다.
  • RedisDocumentHelper 리팩토링: Redis에서 모든 문서를 스캔하던 findAllBrandTagDocumentsfindAllCampaignTagDocuments 메서드가 FT.SEARCH 명령어를 사용하여 관련성 높은 후보 문서를 효율적으로 검색하는 findCandidateBrandsfindCandidateCampaigns 메서드로 대체되었습니다. 검색 실패 시 기존 스캔 방식으로 폴백하는 로직도 포함되었습니다.
  • 매칭 응답 DTO 개선: 매칭 결과 응답에 새로 분류된 사용자 유형과 관련된 태그(userTypeTag) 필드가 추가되어 클라이언트에서 더 풍부한 정보를 활용할 수 있게 되었습니다.
Changelog
  • data/generators/redis_matching_generator.py
    • Redisearch 스키마에 크리에이터 키, 체형, 사이즈, 콘텐츠 시청자 정보 등 새로운 숫자 및 태그 필드를 추가했습니다.
  • data/main.py
    • 더미 데이터 생성 로직의 일부를 주석 처리하여 개발 및 테스트 환경에서 불필요한 데이터 생성을 방지했습니다.
  • src/main/java/com/example/RealMatch/match/application/service/MatchServiceImpl.java
    • 사용자 유형에 따른 태그 매핑 정보를 정의하는 USER_TYPE_TAG_MAP 상수를 추가했습니다.
    • 매칭 검사 주석을 간결하게 업데이트했습니다.
    • 매칭 응답 시 사용자 이름을 닉네임으로 변경하고, 새로 분류된 사용자 유형 태그를 포함하도록 MatchResponseDto 빌더를 수정했습니다.
    • 사용자 태그 데이터를 기반으로 4가지 유형('유연한 연출가', '섬세한 설계자', '재치있는 스토리텔러', '도전적인 실험가')을 분류하는 determineUserType 로직을 대폭 개선했습니다.
    • 브랜드 및 캠페인 매칭 결과를 가져오는 메서드에서 RedisDocumentHelperfindAll 대신 findCandidate 메서드를 사용하도록 변경했습니다.
  • src/main/java/com/example/RealMatch/match/infrastructure/redis/RedisDocumentHelper.java
    • Redisearch 인덱스 이름 상수를 추가했습니다.
    • 사용자 태그를 기반으로 브랜드 및 캠페인 후보를 Redisearch에서 검색하는 findCandidateBrandsfindCandidateCampaigns 메서드를 추가했습니다.
    • 기존의 findAllBrandTagDocumentsfindAllCampaignTagDocuments 메서드를 @Deprecated 처리했습니다.
    • Redisearch 쿼리를 동적으로 생성하는 buildTagOverlapQuerybuildTagClause 헬퍼 메서드를 추가했습니다.
    • Redisearch FT.SEARCH 명령어를 실행하고 결과를 파싱하는 executeSearchparseFtSearchResult 메서드를 구현했습니다. 검색 실패 시 기존 스캔 방식으로 폴백하는 로직을 포함했습니다.
    • 바이트 배열을 문자열로 디코딩하는 decodeBytes 헬퍼 메서드를 추가했습니다.
  • src/main/java/com/example/RealMatch/match/presentation/dto/response/MatchResponseDto.java
    • 사용자 유형과 관련된 태그 목록을 담는 userTypeTag 필드를 추가했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Yoonchulchung Yoonchulchung changed the title [REFACTOR] Redisearch로 매칭 알고리즘 리팩토일 [REFACTOR] Redisearch로 매칭 알고리즘 리팩토링 Feb 11, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR refactors the existing application server-based matching logic to use Redisearch, improving performance by moving computation to Redis, and also introduces a new user type classification algorithm. While the direction of improving performance with Redisearch is positive, a medium-severity security vulnerability has been identified: the new code constructs RediSearch queries by directly concatenating user-influenced data, creating a potential for Redis Injection. This insecure pattern should be addressed to prevent future vulnerabilities. Furthermore, it is recommended to refactor the complex business logic in MatchServiceImpl's determineUserType method into a separate class for improved maintainability and testability, generalize the fallback logic in RedisDocumentHelper for enhanced scalability, and restore the removed Javadoc for the match method for clarity.

Comment on lines +93 to +96
private String buildTagClause(Set<Integer> tags, String fieldName) {
if (tags == null || tags.isEmpty()) {
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The buildTagClause method dynamically constructs a RediSearch query by concatenating user-provided tag values directly into the query string. The tags set, which originates from the user's request (MatchRequestDto), is converted to a string and embedded into the query sent to redisTemplate.execute. This pattern of building queries via string concatenation is a classic injection vulnerability.

While the current implementation uses a Set<Integer> for tags, which mitigates the immediate risk as integers cannot contain malicious characters to alter the query structure, this pattern is inherently insecure. Any future change that allows for string-based tags could immediately expose the application to Redis command injection, allowing an attacker to execute arbitrary commands on the Redis server.

Remediation:
Avoid manual string concatenation for building queries. Use a parameterized query builder if the Redis client library supports it. If not, perform strict validation and sanitization on the input tags to ensure they cannot be used to escape the query structure. For example, ensure tags only contain alphanumeric characters. Since the library in use (spring-data-redis) does not have a native RediSearch query builder, the best approach is to add a comment warning future developers about this risk if they consider changing the data type.

Comment on lines +97 to +99
// ******* //
// 매칭 검사 //
// ******* //
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

match 메서드의 트랜잭션 범위와 동작을 설명하던 기존 Javadoc 주석이 제거되었습니다. 해당 주석은 메서드의 역할을 이해하는 데 매우 유용했습니다. 코드의 명확성과 유지보수성을 위해 이전 Javadoc을 복원하거나, 최소한 메서드의 핵심 동작을 설명하는 주석을 유지하는 것을 제안합니다.

Comment on lines +122 to +132
results = parseFtSearchResult(rawResult, clazz);
log.info("FT.SEARCH found {} candidates. index={}, query={}", results.size(), indexName, query);
} catch (Exception e) {
log.error("FT.SEARCH failed. index={}, query={}, error={}", indexName, query, e.getMessage());
// FT.SEARCH 실패 시 레거시 방식으로 fallback
log.warn("Falling back to KEYS scan for index: {}", indexName);
if (BRAND_INDEX.equals(indexName)) {
@SuppressWarnings("unchecked")
List<T> fallback = (List<T>) findAllDocuments(BRAND_PREFIX, BrandTagDocument.class);
return fallback;
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

FT.SEARCH 실패 시 KEYS 스캔으로 fallback하는 로직은 좋은 방어적 프로그래밍입니다. 다만 현재 구현은 인덱스 이름을 문자열로 비교하고 있어, 새로운 검색 인덱스가 추가될 때마다 코드를 수정해야 하는 단점이 있습니다. 이 부분을 제네릭 타입 Class<T> clazz를 활용하여 좀 더 유연하고 타입-안전하게 개선할 수 있습니다.

// FT.SEARCH 실패 시 레거시 방식으로 fallback
log.warn("Falling back to KEYS scan for index: {}", indexName);
String prefix;
if (BrandTagDocument.class.equals(clazz)) {
    prefix = BRAND_PREFIX;
} else if (CampaignTagDocument.class.equals(clazz)) {
    prefix = CAMPAIGN_PREFIX;
} else {
    log.error("Unsupported class for fallback scan: {}", clazz.getName());
    return Collections.emptyList();
}
return findAllDocuments(prefix, clazz);

이렇게 수정하면 향후 확장 시에도 executeSearch 메서드를 수정할 필요가 없어집니다.

@Yoonchulchung Yoonchulchung merged commit 211bf85 into dev Feb 11, 2026
1 check passed
@Yoonchulchung Yoonchulchung deleted the refactor/372-match-type branch February 11, 2026 07:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant